#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <string>
#include <arpa/inet.h>

#include <tcpserver.h>
#include <log.h>

bool TcpServer::init(int p)
{
    port = p;

    fd = socket(AF_INET, SOCK_STREAM, 0);

    if (fd < 0) {
        log_print("TcpServer::init: socket failed: %s\n",
                  strerror(errno));
        return false;
    }

    int on = 1;
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(int)) < 0) {
        log_print("TcpServer::init: setsockopt failed: %s\n",
                  strerror(errno));
        close(fd);
        fd = -1;
        return false;
    }

    sockaddr_in addr;
    addr.sin_family      = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port        = htons(port);

    if (bind(fd, (struct sockaddr *)&addr, sizeof(sockaddr_in)) < 0) {
        log_print("TcpServer::init: bind failed: %s\n",
                  strerror(errno));
        close(fd);
        fd = -1;
        return false;
    }

    if (listen(fd, 16) < 0) {
        log_print("TcpServer::init: listen failed: %s\n",
                  strerror(errno));
        close(fd);
        fd = -1;
        return false;
    }

    FD_ZERO(&rfds);

    return true;
}

bool TcpServer::try_accept(int max_wait, ClientInfo& ci)
{
    if (fd < 0)
        return false;

    struct timeval wait;

    wait.tv_sec  = max_wait / 1000000;
    wait.tv_usec = max_wait % 1000000;

    FD_SET(fd, &rfds);

    if (select(fd + 1, &rfds, NULL, NULL, &wait) > 0) {
        socklen_t addr_len = sizeof(ci.addr);
        ci.fd = accept(fd, (struct sockaddr *)&ci.addr,
                       &addr_len);
        if (ci.fd < 0) {
            log_print("TcpServer::try_accept: accept failed: %s\n",
                      strerror(errno));
            return false;
        }

        return true;
    }

    return false;
}
